package com.magnusart.scrumppm.model;

import java.util.Observable;

/**
 * A state machine implementation that uses an observable
 * object to announce state changes to the environment.
 * @author Magnus Andersson
 *
 */
public enum States {
	
	/**
	 * These are the valid states, 
	 * when a state is change these will be announced
	 */
	PROJECT_CHOOSER, PROJECT_DETAILS, PROJECT_CREATE_NEW, MENU;

	/**
	 * This defines the initial state, the first state in the list
	 */
	private static States cur = States.values()[0];
	
	/**
	 * An observable object for local use
	 */
	private static final LocalObservable obs = new LocalObservable();
	
	/**
	 * @return The current state the state machine is in
	 */
	public static final States currentState() {
		return cur;
	}
	
	public static final Observable observe() {
		return obs;
	}

	/**
	 * Defines which transitions are valid between states
	 * @author Magnus Andersson
	 *
	 */
	public enum Transition {
		/**
		 * The transitions between states
		 */
		CHOOSER_TO_NEW( PROJECT_CHOOSER, PROJECT_CREATE_NEW ),
		CHOOSER_TO_DETAILS( PROJECT_CHOOSER, PROJECT_DETAILS ), 
		NEW_TO_DETAILS( PROJECT_CREATE_NEW, PROJECT_DETAILS ), 
		DETAILS_TO_NEW( PROJECT_DETAILS, PROJECT_CREATE_NEW ),
		NEW_TO_CHOOSER( PROJECT_CREATE_NEW, PROJECT_CHOOSER),	
		DETAILS_TO_CHOOSER( PROJECT_DETAILS, PROJECT_CHOOSER ),
		MENU_TO_CHOOSER( MENU, PROJECT_CHOOSER ),
		MENU_TO_NEW( MENU, PROJECT_CREATE_NEW );

		/**
		 * Local instance of the source state
		 */
		public final States src;
		public final States dst;

		
		/**
		 * @param src The source state for this transition
		 * @param dst The destination state for this transition
		 */
		private Transition( States src, States dst ) {
			this.src = src;
			this.dst = dst;
		}
		
		/**
		 * If we try to go from menu to a transition we need to search for a 
		 * transition that matches from the current state to the destination state.
		 * 
		 * @param dst The destination state.
		 * @return
		 */
		private static void menuTrans( States dst ) {
			for( Transition t : Transition.values() ) {
				if( t.src == cur && t.dst == dst ) {
					go( t );
					break;
				}
			}
		}


		private static void go( Transition trans) {
			cur = trans.dst; // Set new current state
			obs.announce( trans ); // Announce the new state
		}	
		
		/**
		 * Tries to complete a transition, a new current state is set if successful
		 * @param trans Transition 
		 * @return True if the transition is successful
		 */
		public static final boolean to( Transition trans ) {
			if( trans.src.equals( cur ) ) {		
				go( trans );		
				return true;
			} else if( trans.src.equals( MENU ) ) {
				menuTrans( trans.dst );
			}
			return false;
		}

	}
}
